Skip to content

Conversation

@arturobernalg
Copy link
Member

@arturobernalg arturobernalg commented Dec 27, 2025

This change adds an optional hard cap on the number of pending request execution commands queued per HTTP/2 connection. When the per-connection limit is reached, new submissions fail fast with RejectedExecutionException and the exchange handler releases its resources immediately. The cap is configured via H2MultiplexingRequesterBootstrap and passed into H2MultiplexingRequester at construction time to keep requester configuration immutable and avoid API incompatibilities.

@arturobernalg arturobernalg requested a review from ok2c December 27, 2025 18:14
@ok2c
Copy link
Member

ok2c commented Dec 28, 2025

@arturobernalg I do not like this approach. There is already a command queue built into the IOSession. We should leverage that queue instead of creating another layer of complexity. What we need is to extend IOSession interface to expose information about already pending commands and optionally reject commands if the queue is over a particular limit.

@arturobernalg arturobernalg force-pushed the max-requests-per-connection branch from de6f206 to b777f5d Compare December 29, 2025 17:58
@arturobernalg
Copy link
Member Author

arturobernalg commented Dec 29, 2025

@arturobernalg I do not like this approach. There is already a command queue built into the IOSession. We should leverage that queue instead of creating another layer of complexity. What we need is to extend IOSession interface to expose information about already pending commands and optionally reject commands if the queue is over a particular limit.

@ok2c . Sorry about the churn — I’m still getting up to speed on this part of the reactor stack and my previous commit took a wrong turn. I dropped the extra wrapper and enforce the cap via the existing IOSession command queue. I extended IOSession with a bounded enqueue + pending count and delegated through InternalDataChannel and SSLIOSession so the limit is not bypassed by decorators.

* @return {@code true} if the command was enqueued, {@code false} otherwise.
* @since 5.5
*/
default boolean enqueue(final Command command, final Command.Priority priority, final int maxPendingCommands) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg I would not do that on the i/o session level. Different protocol handlers may have different restrictions. 10 concurrent requests may be perfectly OK for HTTP/2 and too much for HTTP/1.1. Please move this logic to individual protocol handlers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ok2c I just drop the IOSession-level enqueue/queue-limit API and keep the cap/reject logic strictly in the HTTP/2 requester/handler layer as requested.

@arturobernalg arturobernalg force-pushed the max-requests-per-connection branch from 3cbecbe to 639e632 Compare December 30, 2025 18:53
@arturobernalg arturobernalg requested a review from ok2c December 30, 2025 18:54
@arturobernalg arturobernalg force-pushed the max-requests-per-connection branch from 9c8911c to e6cb499 Compare January 1, 2026 13:20
@arturobernalg arturobernalg requested a review from ok2c January 1, 2026 13:24
Copy link
Member

@ok2c ok2c left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg Conceptually everything looks good to me. However as far as I can see you are also mixing in some code optimization into the same change-set.

Could you please do this?

  1. Spin the optimization code into a separate pull request. It should get merged in first.
  2. Re-base this pull request off it
  3. Provide the same logic to HttpAsyncRequester for consistency.

@arturobernalg arturobernalg force-pushed the max-requests-per-connection branch 7 times, most recently from 130dedf to 8aa41be Compare January 2, 2026 10:32
@arturobernalg arturobernalg requested a review from ok2c January 2, 2026 11:28
@arturobernalg
Copy link
Member Author

@arturobernalg Conceptually everything looks good to me. However as far as I can see you are also mixing in some code optimization into the same change-set.

Could you please do this?

1. Spin the optimization code into a separate pull request. It should get merged in first.

2. Re-base this pull request off it

3. Provide the same logic to `HttpAsyncRequester` for consistency.

@ok2c Please another pass

Copy link
Member

@ok2c ok2c left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg Almost there. Just a few comments.

private final H2ConnPool connPool;

/**
* Hard cap on per-connection queued / in-flight requests.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg Let's use the term "command" instead of "request" here to make sure there is no confusion with HTTP requests.

final int current = ioSession.getPendingCommandCount();
if (current >= 0 && current >= max) {
exchangeHandler.failed(new RejectedExecutionException(
"Maximum number of pending requests per connection reached (max=" + max + ")"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg Same as above. Let say "command" instead of "request" here


private IOReactorMetricsListener threadPoolListener;

private int maxRequestsPerConnection;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,245 @@
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg Let's drop all examples and integration tests. They look heavy and require a specially crafted server side listener. I wish we had unit test coverage for HttpAsyncRequester / H2MultiplexingRequester but we do not.

This functionality can have integration tests and examples in client along with other client request per connection policy configuration.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ok2c done

@arturobernalg arturobernalg force-pushed the max-requests-per-connection branch from 7ddd29a to 756d5a4 Compare January 2, 2026 18:11
@arturobernalg arturobernalg requested a review from ok2c January 2, 2026 18:11
@ok2c ok2c changed the title Cap total number of concurrent requests per HTTP/2 connection Cap total number of concurrent commands per connection Jan 2, 2026
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

class TestIOSessionImplMaxPendingCommands {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg I am not sure what this test is actually testing. Let's drop it and the other test as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted

Restore per-session command count and use it in H2MultiplexingRequester to
fail fast when the per-connection command queue exceeds the configured limit.
@arturobernalg arturobernalg force-pushed the max-requests-per-connection branch from 186431e to 22d2ea5 Compare January 2, 2026 21:25
@arturobernalg arturobernalg requested a review from ok2c January 2, 2026 21:26
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class TestH2MultiplexingRequesterMaxRequestsPerConnection {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arturobernalg This test should be moved to client and be made a part of a larger suite of related integration tests, but if you want to keep it here, so be it, but at least be consistent and provide a similar one for HttpAsyncRequester.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants